package com.cai.service.impl;

import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.cai.common.result.ResultVO;
import com.cai.common.result.StatusCode;
import com.cai.dao.repository.IUserRepository;
import com.cai.entity.DO.User;
import com.cai.entity.DTO.UserLoginParams;
import com.cai.entity.PO.LoginUserInfo;
import com.cai.entity.PO.UserDetailsImpl;
import com.cai.ex.ServiceException;
import com.cai.provider.UsernamePasswordAuthenticationProvider;
import com.cai.service.UserService;
import com.cai.utils.JwtUtils;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.data.redis.core.ValueOperations;
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
import org.springframework.security.core.Authentication;
import org.springframework.stereotype.Service;

import javax.servlet.http.HttpServletRequest;
import java.util.Date;
import java.util.Objects;
import java.util.concurrent.TimeUnit;

/**
 * @Title: UserServiceImpl
 * @Author RuoLi
 * @Package com.cai.service.impl
 * @Date 2024/1/12 10:20
 * @description:
 */
@Service
@Slf4j
public class UserServiceImpl implements UserService {
    @Autowired
    private IUserRepository userRepository;
    @Autowired
    private UsernamePasswordAuthenticationProvider usernamePasswordAuthenticationProvider;
    @Autowired
    private JwtUtils jwtUtils;
    @Autowired
    private RedisTemplate<String, Object> redisTemplate;
    @Value("${jwt.expires}")
    private long tokenExpireTime;

    @Override
    public ResultVO login(UserLoginParams params, HttpServletRequest request) {
        ValueOperations<String, Object> opsForValue = redisTemplate.opsForValue();

        UsernamePasswordAuthenticationToken authenticationToken = new UsernamePasswordAuthenticationToken(params.getUsername(), params.getPassword());
        Authentication authenticate = usernamePasswordAuthenticationProvider.authenticate(authenticationToken);

        if (Objects.isNull(authenticate)) return ResultVO.fail(StatusCode.FAIL, "登录失败");

        UserDetailsImpl userDetails = (UserDetailsImpl) authenticate.getPrincipal();

        //验证是否已经登录
        if (isLogin(userDetails.getMyPrincipal().getId())) {
            return ResultVO.fail(StatusCode.FAIL, "当前用户已登录");
        }
        //向缓存中存入用户数据
        long userId = userDetails.getMyPrincipal().getId();
        //存放用户数据
        opsForValue.set("login:" + userId, userDetails, tokenExpireTime, TimeUnit.MINUTES);
        //存放token数据
        String token = jwtUtils.createToken(userId);
        opsForValue.set("token:" + userId, token, tokenExpireTime, TimeUnit.MINUTES);
        //存放用户信息的登录数据
        LoginUserInfo loginUserInfo = new LoginUserInfo();
        loginUserInfo.setLoginAddress(request.getRemoteAddr());
        loginUserInfo.setUserHost(request.getRemoteHost());
        loginUserInfo.setUserAgent(request.getHeader("User-Agent"));
        loginUserInfo.setLoginTime(new Date());
        opsForValue.set(token, loginUserInfo, tokenExpireTime, TimeUnit.MINUTES);
        return ResultVO.ok(token);
    }

    @Override
    public void logout(HttpServletRequest request) {
        //获取token
        String token = request.getHeader("token");
        if (Objects.isNull(token)) throw new ServiceException("token为空");
        if (!jwtUtils.validate(token)) throw new ServiceException("token解析失败");
        String userId = jwtUtils.parseToken(token).getSubject();
        String loginKey = "login:" + userId;
        String tokenKey = "token:" + userId;
        if (Boolean.TRUE.equals(redisTemplate.hasKey(token))) redisTemplate.delete(token);
        if (Boolean.TRUE.equals(redisTemplate.hasKey(loginKey))) redisTemplate.delete(loginKey);
        if (Boolean.TRUE.equals(redisTemplate.hasKey(tokenKey))) redisTemplate.delete(tokenKey);
    }

    private boolean isLogin(Long id) {
        return Boolean.TRUE.equals(redisTemplate.hasKey("login:" + id));
    }

    @Override
    public User getUserByUsername(String username) {
        QueryWrapper<User> userQueryWrapper = new QueryWrapper<>();
        userQueryWrapper.eq("username", username);
        User user = userRepository.getOne(userQueryWrapper);
        if (Objects.isNull(user)) throw new ServiceException("用户不存在");
        return user;
    }

    @Override
    public String getRoleByUsername(String username) {
        return userRepository.getRoleByUsername(username);
    }
}
